package com.hwtx.form.plugin;

import com.google.common.collect.Maps;
import com.hwtx.form.annotation.Action;
import com.hwtx.form.annotation.FormType;
import com.hwtx.form.annotation.Log;
import com.hwtx.form.annotation.OperationType;
import com.hwtx.form.domain.def.FormDef;
import com.hwtx.form.domain.ds.DataSourceService;
import com.hwtx.form.domain.handle.core.DefaultFormDataHandle;
import com.hwtx.form.domain.handle.core.DefaultFormDataReadHandle;
import com.hwtx.form.domain.handle.FormDataHandle;
import com.hwtx.form.domain.repo.FormRepo;
import com.hwtx.form.domain.vo.FormData;
import com.hwtx.form.domain.vo.FormRepData;
import com.hwtx.form.domain.vo.ResultVo;
import com.hwtx.form.query.FormValueQuery;
import com.zaxxer.hikari.HikariDataSource;
import io.geekidea.boot.framework.exception.BusinessException;
import jakarta.annotation.Resource;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.hwtx.form.domain.FormConstants.*;

@FormType(value = "dsExt", comment = "数据源扩展")
@Slf4j
public class DsExtFormDataHandle implements FormDataHandle {

    @Resource
    private DefaultFormDataHandle dataHandle;
    @Resource
    private DefaultFormDataReadHandle defaultFormDataReadHandle;
    @Resource
    private FormRepo formRepo;
    @Resource
    private DataSourceService dataSourceService;

    @Override
    public void init() {
        Long formId = formRepo.getFormIdByName("ds");
        if (formId == null) {
            throw new BusinessException("无法获取数据源表单");
        }
        try {
            FormDef formDef = formRepo.getFormDef(formId);
            Map<String, Object> filters = Maps.newHashMap();
            filters.put("status", (byte) 1);
            List<Map<String, Object>> dsSet = defaultFormDataReadHandle.getCommonFormData(formDef, filters);
            for (Map<String, Object> ds : dsSet) {
                if (!Objects.equals(ds.get("name"), DEFAULT_DS_NAME)) {
                    DsInfo dsInfo = getDsInfo(formDef, new FormRepData.MapFormRepData(ds));
                    _reg(dsInfo);
                }
            }
        } catch (Exception e) {
            log.error("", e);
        }
    }

    @Override
    public void destroy() {
        Long formId = formRepo.getFormIdByName("ds");
        if (formId == null) {
            throw new BusinessException("无法获取数据源表单");
        }
        try {
            FormDef formDef = formRepo.getFormDef(formId);
            Map<String, Object> filters = Maps.newHashMap();
            filters.put("status", (byte) 1);
            List<Map<String, Object>> dsSet = defaultFormDataReadHandle.getCommonFormData(formDef, filters);
            for (Map<String, Object> ds : dsSet) {
                if (!Objects.equals(ds.get("name"), DEFAULT_DS_NAME)) {
                    DsInfo dsInfo = getDsInfo(formDef, new FormRepData.MapFormRepData(ds));
                    _unReg(dsInfo);
                }
            }
        } catch (Exception e) {
            log.error("", e);
        }
    }

    @Action("test")
    @Log(type = OperationType.GET, value = "测试数据源")
    public ResultVo testConnection(Map<String, Object> formData) {
        try {
            beforeCheck(formData);
            DsInfo dsInfo = getDsInfo(formData);
            if (!Objects.equals(dsInfo.getName(), DEFAULT_DS_NAME)) {
                DriverManagerDataSource dataSource = new DriverManagerDataSource();
                dataSource.setDriverClassName(dsInfo.getDriver());
                dataSource.setUrl(dsInfo.getUrl());
                dataSource.setUsername(dsInfo.getUsername());
                dataSource.setPassword(dsInfo.getPassword());
                return ResultVo.builder().success(dataSource.getConnection().isValid(2000)).msg("测试成功").build();
            }
            return ResultVo.builder().success(true).msg("测试成功").build();
        } catch (Exception e) {
            log.error("测试数据源失败", e);
        }
        return ResultVo.builder().success(false).msg("测试失败").build();
    }

    @Action
    @Log(type = OperationType.UPDATE, value = "注册数据源")
    public ResultVo reg(Map<String, Object> formData) {
        beforeCheck(formData);
        try {
            _reg(getDsInfo(formData));
            return ResultVo.builder().success(true).msg("加载成功").build();
        } catch (Exception e) {
            throw new BusinessException("加载失败");
        }
    }

    private void _reg(DsInfo dsInfo) throws Exception {
        if (Objects.equals(dsInfo.getName(), DEFAULT_DS_NAME)) {
            return;
        }
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(dsInfo.getUrl());
        dataSource.setUsername(dsInfo.getUsername());
        dataSource.setPassword(dsInfo.getPassword());
        dataSource.setDriverClassName(dsInfo.getDriver());
        dataSourceService.addDataSource(JDBC_PREFIX + dsInfo.getName(), dataSource);
        dsInfo.getFormRepData().getData().put("status", 1);
        dataHandle.saveFormData(dsInfo.getFormDef(), FormData.builder().data(dsInfo.getFormRepData().getData()).build());
        log.info("数据源加载成功,ds = {}", dsInfo.getName());
    }

    @Action
    @Log(type = OperationType.UPDATE, value = "卸载数据源")
    public ResultVo unReg(Map<String, Object> formData) {
        beforeCheck(formData);
        try {
            DsInfo dsInfo = getDsInfo(formData);
            _unReg(dsInfo);
            return ResultVo.builder().success(true).msg("卸载成功").build();
        } catch (Exception e) {
            throw new BusinessException("卸载失败");
        }
    }

    private void _unReg(DsInfo dsInfo) throws Exception {
        if (Objects.equals(dsInfo.getName(), DEFAULT_DS_NAME)) {
            return;
        }
        dataSourceService.removeDataSource(JDBC_PREFIX + dsInfo.getName());
        dsInfo.getFormRepData().getData().put("status", 2);
        dataHandle.saveFormData(dsInfo.getFormDef(), FormData.builder().data(dsInfo.getFormRepData().getData()).build());
    }


    private void beforeCheck(Map<String, Object> formData) {
        if (formData.get("id") == null || StringUtils.isBlank(formData.get("id").toString())) {
            throw new BusinessException("数据源id不能为空");
        }
        if (formData.get(FORM_ID) == null || StringUtils.isBlank(formData.get(FORM_ID).toString())) {
            throw new BusinessException("数据源表单id不能为空");
        }
    }

    private DsInfo getDsInfo(Map<String, Object> formData) {
        FormDef formDef = formRepo.getFormDef(Long.valueOf(formData.get(FORM_ID).toString()));
        FormValueQuery query = new FormValueQuery();
        query.setValueIds(Collections.singletonList(Long.valueOf(formData.get("id").toString())));
        FormRepData.MapFormRepData formRepData = dataHandle.getFormData(formDef, query);
        return getDsInfo(formDef, formRepData);
    }

    private DsInfo getDsInfo(FormDef formDef, FormRepData.MapFormRepData formRepData) {
        Map<String, Object> data = formRepData.getData();
        String name = data.get("name").toString();
        if (!Objects.equals(DEFAULT_DS_NAME, name)) {
            String url = data.get("url").toString();
            String username = data.get("user_name").toString();
            String password = data.get("pwd").toString();
            String type = data.get("type").toString();
            if (!Objects.equals(type, "mysql")) {
                throw new BusinessException("数据源类型错误，只支持mysql");
            }
            return new DsInfo(name, url, username, password, type,
                    "com.mysql.cj.jdbc.Driver", formRepData, formDef);
        }
        return DsInfo.builder().name(DEFAULT_DS_NAME).build();
    }

    @Builder
    @Getter
    static class DsInfo {
        private String name;
        private String url;
        private String username;
        private String password;
        private String type;
        private String driver;
        private FormRepData.MapFormRepData formRepData;
        private FormDef formDef;
    }
}